﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
#if TASKSUPPORT
using System.Threading.Tasks;
#endif
using System.Windows;
using JetBrains.Annotations;
using MugenMvvmToolkit.Interfaces;
using MugenMvvmToolkit.Interfaces.ViewModels;
using MugenMvvmToolkit.Models;
using MugenMvvmToolkit.Utils;
using MugenMvvmToolkit.ViewModels;

namespace MugenMvvmToolkit.Infrastructure
{
    /// <summary>
    ///     Represents the global class for default settings.
    /// </summary>
    public static class GlobalSettings
    {
        #region Fields

        private static readonly Dictionary<Type, ConstructorInfo> EntityConstructorInfos;
        private static IThreadManager _defaultThreadManager;
        private static bool? _isInDesignMode;
        private static TimeSpan _deadLockWaitTime;
        private static GetCloseableFacadeDelegate _closeableViewModelFacadeFactory;
        private static IActivationPolicy _defaultActivationPolicy;

        #endregion

        #region Constructors

        /// <summary>
        ///     Initializes a new instance of the <see cref="GlobalSettings" /> class.
        /// </summary>
        static GlobalSettings()
        {
            EntityConstructorInfos = new Dictionary<Type, ConstructorInfo>();
            SetDefaultSettings();
        }

        #endregion

        #region Properties

        /// <summary>
        ///     Gets or sets the factory that creates an instance of <see cref="CloseableViewModelFacade" />.
        /// </summary>
        [NotNull]
        public static GetCloseableFacadeDelegate CloseableViewModelFacadeFactory
        {
            private get { return _closeableViewModelFacadeFactory; }
            set
            {
                Should.PropertyBeNotNull(value, "CloseableViewModelFacadeFactory");
                _closeableViewModelFacadeFactory = value;
            }
        }

        /// <summary>
        ///     Gets or sets the factory that creates a empty instance of editable entity.
        /// </summary>
        public static Func<Type, object> DefaultEntityFactory { get; set; }

        /// <summary>
        ///     Gets or sets the default <see cref="IThreadManager" />.
        /// </summary>
        [NotNull]
        public static IThreadManager DefaultThreadManager
        {
            get { return _defaultThreadManager; }
            set
            {
                Should.PropertyBeNotNull(value, "DefaultThreadManager");
                _defaultThreadManager = value;
            }
        }

        /// <summary>
        ///     Gets the value indicating whether the control is in design mode (running under Blend or Visual Studio).
        /// </summary>
        public static bool IsDesignMode
        {
            get
            {
                if (_isInDesignMode.HasValue)
                    return _isInDesignMode.Value;
                try
                {
#if SILVERLIGHT
                    _isInDesignMode = DesignerProperties.IsInDesignTool;
#endif
#if WPF
                    DependencyProperty prop = DesignerProperties.IsInDesignModeProperty;
                    _isInDesignMode =
                        (bool)
                        DependencyPropertyDescriptor.FromProperty(prop, typeof(FrameworkElement))
                                                    .Metadata.DefaultValue;
                    if (!_isInDesignMode.Value
                        && Process.GetCurrentProcess().ProcessName.StartsWith("devenv", StringComparison.Ordinal))
                    {
                        _isInDesignMode = true;
                    }
#endif
#if NETFX_CORE
                    _isInDesignMode = Windows.ApplicationModel.DesignMode.DesignModeEnabled;
#endif
                }
                catch (Exception)
                {
                    _isInDesignMode = false;
                }

                return _isInDesignMode.Value;
            }
        }

        /// <summary>
        ///     Gets or sets property, which responsible for shows additional information in output.
        /// </summary>
        public static bool IsDebugMode { get; set; }

        /// <summary>
        ///     Specifies the execution mode for <c>FilterableNotifiableCollection</c> by default, if not set explicitly.
        /// </summary>
        public static ExecutionType FilterableCollectionExecutionMode { get; set; }

        /// <summary>
        ///     Specifies the execution mode for <c>SyncronizedNotifiableCollection</c> by default, if not set explicitly.
        /// </summary>
        public static ExecutionType SyncronizedCollectionExecutionMode { get; set; }

        /// <summary>
        ///     Specifies the execution mode for <c>FilterableNotifiableCollection</c> by default, if not set explicitly.
        /// </summary>
        public static ExecutionType OrderedCollectionExecutionMode { get; set; }

        /// <summary>
        ///     Specifies the count mode for <c>NotifiableCollection</c> by default, if not set explicitly.
        /// </summary>
        public static NotificationCollectionCountType NotificationCollectionCountMode { get; set; }

        /// <summary>
        ///     Specifies the execution mode for <c>RaiseCanExecuteChanged</c> method in <c>IRelayCommand</c> by default, if not set explicitly.
        /// </summary>
        public static ExecutionType CommandCanExecuteExecutionMode { get; set; }

        /// <summary>
        ///     Specifies the execution mode for <c>Execute</c> method in <c>IRelayCommand</c> by default, if not set explicitly.
        /// </summary>
        public static CommandExecutionType CommandExecutionMode { get; set; }

        /// <summary>
        ///     Specifies the execution mode for raise property changed event in <c>NotifyPropertyChangedBase</c> by default, if not set explicitly.
        /// </summary>
        public static ExecutionType PropertyChangeExecutionMode { get; set; }

        /// <summary>
        ///     Gets or sets the property, which authorizes the use of weak-delegates in IRelayCommand.
        /// </summary>
        public static bool UseWeakDelegate { get; set; }

#if WPF
        /// <summary>
        ///     Enabling subscribe to CommandManager.RequerySuggested event, default value for all <c>IRelayCommand</c>, if not set explicitly.
        /// </summary>
        public static bool UseCommandManager { get; set; }

#endif
        /// <summary>
        ///     Gets or sets the value that responsible for listen messages in child view models, if not set explicitly.
        /// </summary>
        public static ListenType ListenViewModelMode { get; set; }

        /// <summary>
        ///     Gets or sets value that responsible for detect dead locks.
        /// </summary>
        public static bool DeadLockDetectionEnabled { get; set; }

        /// <summary>
        ///     Gets or sets value that responsible for checking the cyclic validation. 
        ///     If <c>true</c> view model throw an exception if the cyclic validation will be detected, otherwise <c>false</c>.
        /// </summary>
        public static bool CyclicValidationDetectionEnabled { get; set; }

        /// <summary>
        /// Gets or sets the default activation policy.
        /// </summary>
        [NotNull]
        public static IActivationPolicy DefaultActivationPolicy
        {
            get
            {
                if (_defaultActivationPolicy == null)
                    _defaultActivationPolicy = ActivationPolicy.WithView;
                return _defaultActivationPolicy;
            }
            set
            {
                Should.PropertyBeNotNull(value, "DefaultActivationPolicy");
                _defaultActivationPolicy = value;
            }
        }

        /// <summary>
        /// Gets or sets the default view-model settings.
        /// </summary>
        public static IViewModelSettings DefaultViewModelSettings { get; set; }

        /// <summary>
        /// Gets or sets the root <see cref="IIocAdapter"/>.
        /// </summary>
        public static IIocAdapter RootIocAdapter { get; set; }

#if TASKSUPPORT
        /// <summary>
        /// Responsible for handling errors in the WithBusyIndicator method. 
        /// If true errors will be processed using the <see cref="ITaskExceptionHandler"/> interface; otherwise false.
        /// </summary>
        public static bool HandleTaskExceptionWithBusyIndicator { get; set; }
#endif
        /// <summary>
        ///     Gets or sets value that responsible for wait lock.
        /// </summary>
        public static TimeSpan DeadLockWaitTime
        {
            get { return _deadLockWaitTime; }
            set
            {
                var num = (long)value.TotalMilliseconds;
                if (num < -1L || num > int.MaxValue)
                    Thrower.IntNonNegativeOutOfRange();
                _deadLockWaitTime = value;
            }
        }

        #endregion

        #region Methods

#if TASKSUPPORT
        /// <summary>
        /// Creates an instance of <see cref="CloseableViewModelFacade" />.
        /// </summary>        
        public static CloseableViewModelFacade GetCloseableFacade<T>([NotNull]T closeableViewModel, Predicate<object> canExecutCloseCommand = null, Func<object, Task<bool>> onClosing = null, Action<object> onClosed = null)
                    where T : ViewModelBase, ICloseableViewModel
        {
            Should.NotBeNull(closeableViewModel, "closeableViewModel");
            return CloseableViewModelFacadeFactory(closeableViewModel, closeableViewModel.OnPropertyChanged,
                canExecutCloseCommand, onClosing, onClosed);
        }

        /// <summary>
        /// Creates an instance of <see cref="CloseableViewModelFacade" />.
        /// </summary>        
        public static CloseableViewModelFacade GetCloseableFacade([NotNull]ICloseableViewModel closeableViewModel, [NotNull] Action<string> propertyChangedCallback, Predicate<object> canExecutCloseCommand = null,
         Func<object, Task<bool>> onClosing = null, Action<object> onClosed = null)
        {
            return CloseableViewModelFacadeFactory(closeableViewModel, propertyChangedCallback, canExecutCloseCommand, onClosing, onClosed);
        }
#else
        /// <summary>
        /// Creates an instance of <see cref="CloseableViewModelFacade" />.
        /// </summary>        
        public static CloseableViewModelFacade GetCloseableFacade<T>([NotNull]T closeableViewModel, Predicate<object> canExecutCloseCommand = null, Func<object, IOperationResult<bool>> onClosing = null, Action<object> onClosed = null)
                    where T : ViewModelBase, ICloseableViewModel
        {
            Should.NotBeNull(closeableViewModel, "closeableViewModel");
            return CloseableViewModelFacadeFactory(closeableViewModel, closeableViewModel.OnPropertyChanged,
                canExecutCloseCommand, onClosing, onClosed);
        }

        /// <summary>
        /// Creates an instance of <see cref="CloseableViewModelFacade" />.
        /// </summary>        
        public static CloseableViewModelFacade GetCloseableFacade([NotNull]ICloseableViewModel closeableViewModel, [NotNull] Action<string> propertyChangedCallback, Predicate<object> canExecutCloseCommand = null,
        Func<object, IOperationResult<bool>> onClosing = null, Action<object> onClosed = null)
        {
            return CloseableViewModelFacadeFactory(closeableViewModel, propertyChangedCallback, canExecutCloseCommand, onClosing, onClosed);
        }
#endif
        /// <summary>
        ///     Gets the factory that creates an instance of <see cref="CloseableViewModelFacade" />.
        /// </summary>
        public static GetCloseableFacadeDelegate GetCloseableViewModelFacadeFactory()
        {
            return CloseableViewModelFacadeFactory;
        }

        internal static void SetRootIocAdapterWithSettings(IIocAdapter iocAdapter)
        {
            Should.NotBeNull(iocAdapter, "iocAdapter");
            RootIocAdapter = iocAdapter;
            DefaultViewModelSettings = iocAdapter.CanResolve(typeof(IViewModelSettings))
                ? iocAdapter.Get<IViewModelSettings>()
                : new DefaultViewModelSettings();
        }

        internal static TimeSpan GetLockTimeout()
        {
            if (DeadLockDetectionEnabled)
                return DeadLockWaitTime;
            return TimeSpan.FromMilliseconds(int.MaxValue);
        }

        internal static void SetDefaultSettings()
        {
            _closeableViewModelFacadeFactory = (model, propertyCallback, command, closing, closed) => new CloseableViewModelFacade(model, propertyCallback, command, closing, closed);
            _defaultThreadManager = new SynchronousThreadManager();
#if WPF
            UseCommandManager = true;
#endif
            NotificationCollectionCountMode = NotificationCollectionCountType.CollectionIntefaceReturnsNotificationCount |
                                              NotificationCollectionCountType.OnlyInUiThread;
            SyncronizedCollectionExecutionMode = ExecutionType.AsynchronousInUi;
            FilterableCollectionExecutionMode = ExecutionType.AsynchronousInUi;
            OrderedCollectionExecutionMode = ExecutionType.AsynchronousInUi;

            CommandExecutionMode = CommandExecutionType.CanExecuteBeforeExecute;
            CommandCanExecuteExecutionMode = ExecutionType.AsynchronousInUi;
            PropertyChangeExecutionMode = ExecutionType.AsynchronousInUi;
            UseWeakDelegate = false;
            ListenViewModelMode = ListenType.Child;
            CyclicValidationDetectionEnabled = true;
            DeadLockDetectionEnabled = true;
            DeadLockWaitTime = TimeSpan.FromSeconds(20);
            DefaultEntityFactory = DefaultEntityFactoryMethod;
#if TASKSUPPORT
            HandleTaskExceptionWithBusyIndicator = true;
#endif
        }

        private static object DefaultEntityFactoryMethod(Type type)
        {
            Should.NotBeNull(type, "type");
            ConstructorInfo constructor;
            lock (EntityConstructorInfos)
            {
                if (!EntityConstructorInfos.TryGetValue(type, out constructor))
                {
#if NETFX_CORE                
                    constructor = type
                    .GetTypeInfo()
                    .DeclaredConstructors
                    .FirstOrDefault(info => info.IsPublic && !info.IsStatic && info.GetParameters().Length == 0);
#else
                    constructor = type.GetConstructor(Type.EmptyTypes);
#endif
                    EntityConstructorInfos[type] = constructor;
                }
            }
            if (constructor == null)
                return null;
            return constructor.Invoke(null);
        }

        #endregion
    }
}